/*
 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "core/interfaces/native/implementation/navigation_context.h"
#include "core/interfaces/native/implementation/navigation_transition_proxy_peer.h"
#include "core/interfaces/native/utility/callback_helper.h"
#include "core/interfaces/native/utility/converter.h"
#include "core/interfaces/native/utility/reverse_converter.h"
#include "core/interfaces/native/utility/validators.h"
#include "arkoala_api_generated.h"

namespace Nav = OHOS::Ace::NG::GeneratedModifier::NavigationContext;

namespace OHOS::Ace::NG::Converter {
struct NavContentInfo {
    std::optional<std::string> name = "";
    int32_t index = -1;
    std::optional<NavDestinationMode> mode = NavDestinationMode::STANDARD;
    std::optional<void*> param = nullptr; // temp stub for CustomObject
    std::optional<std::string> navDestinationId = "";
};

template<>
inline void* Convert(const Ark_Object& src)
{
    return new NavigationTransitionProxyPeer();
}

template<>
NavContentInfo Convert(const Ark_NavContentInfo& src)
{
    NavContentInfo dst;
    dst.name = Converter::OptConvert<std::string>(src.name);
    dst.index = Converter::Convert<int32_t>(src.index);
    dst.mode = Converter::OptConvert<NavDestinationMode>(src.mode);
    dst.param = Converter::OptConvert<void*>(src.param);
    dst.navDestinationId = Converter::OptConvert<std::string>(src.navDestinationId);
    return dst;
}
}

namespace OHOS::Ace::NG::GeneratedModifier {
namespace NavigationTransitionProxyAccessor {
void DestroyPeerImpl(Ark_NavigationTransitionProxy peer)
{
    delete peer;
}
Ark_NavigationTransitionProxy ConstructImpl()
{
    return new NavigationTransitionProxyPeer();
}
Ark_NativePointer GetFinalizerImpl()
{
    return reinterpret_cast<void *>(&DestroyPeerImpl);
}
void FinishTransitionImpl(Ark_NavigationTransitionProxy peer)
{
    CHECK_NULL_VOID(peer && peer->handler);
    peer->handler->FireFinishCallback();
}
Ark_NavContentInfo GetFromImpl(Ark_NavigationTransitionProxy peer)
{
    CHECK_NULL_RETURN(peer && peer->handler, Ark_NavContentInfo());
    auto navDestinationContext = peer->handler->GetPreDestinationContext();
    CHECK_NULL_RETURN(navDestinationContext, Ark_NavContentInfo());
    return Converter::ArkValue<Ark_NavContentInfo>(navDestinationContext);
}
void SetFromImpl(Ark_NavigationTransitionProxy peer,
                 const Ark_NavContentInfo* from)
{
    CHECK_NULL_VOID(peer && from && peer->handler);
    auto navDestinationContext = peer->handler->GetPreDestinationContext();
    CHECK_NULL_VOID(navDestinationContext);
    auto navPathInfo = AceType::DynamicCast<Nav::JSNavPathInfoStatic>(navDestinationContext->GetNavPathInfo());
    CHECK_NULL_VOID(navPathInfo);
    navPathInfo->SetName(Converter::OptConvert<std::string>(from->name).value_or(""));
    navPathInfo->SetParam(Converter::OptConvert<Nav::ExternalData>(from->param).value_or(Nav::ExternalData{}));
    navDestinationContext->SetIndex(Converter::Convert<int32_t>(from->index));
    if (from->mode.tag != InteropTag::INTEROP_TAG_UNDEFINED) {
        navDestinationContext->SetMode(static_cast<NavDestinationMode>(from->mode.value));
    } else {
        navDestinationContext->SetMode(NavDestinationMode::STANDARD);
    }
    auto idString = Converter::OptConvert<std::string>(from->navDestinationId).value_or("0");
    navDestinationContext->SetNavDestinationId(std::atol(idString.c_str()));
}
Ark_NavContentInfo GetToImpl(Ark_NavigationTransitionProxy peer)
{
    CHECK_NULL_RETURN(peer && peer->handler, Ark_NavContentInfo());
    auto navDestinationContext = peer->handler->GetTopDestinationContext();
    CHECK_NULL_RETURN(navDestinationContext, Ark_NavContentInfo());
    return Converter::ArkValue<Ark_NavContentInfo>(navDestinationContext);
}
void SetToImpl(Ark_NavigationTransitionProxy peer,
               const Ark_NavContentInfo* to)
{
    CHECK_NULL_VOID(peer && to && peer->handler);
    auto navDestinationContext = peer->handler->GetTopDestinationContext();
    CHECK_NULL_VOID(navDestinationContext);
    auto navPathInfo = AceType::DynamicCast<Nav::JSNavPathInfoStatic>(navDestinationContext->GetNavPathInfo());
    CHECK_NULL_VOID(navPathInfo);
    navPathInfo->SetName(Converter::OptConvert<std::string>(to->name).value_or(""));
    navPathInfo->SetParam(Converter::OptConvert<Nav::ExternalData>(to->param).value_or(Nav::ExternalData{}));
    navDestinationContext->SetIndex(Converter::Convert<int32_t>(to->index));
    if (to->mode.tag != InteropTag::INTEROP_TAG_UNDEFINED) {
        navDestinationContext->SetMode(static_cast<NavDestinationMode>(to->mode.value));
    } else {
        navDestinationContext->SetMode(NavDestinationMode::STANDARD);
    }
    auto idString = Converter::OptConvert<std::string>(to->navDestinationId).value_or("0");
    navDestinationContext->SetNavDestinationId(std::atol(idString.c_str()));
}
Opt_Boolean GetIsInteractiveImpl(Ark_NavigationTransitionProxy peer)
{
    auto invalid = Converter::ArkValue<Opt_Boolean>();
    CHECK_NULL_RETURN(peer && peer->handler, invalid);
    return Converter::ArkValue<Opt_Boolean>(peer->handler->GetInteractive());
}
void SetIsInteractiveImpl(Ark_NavigationTransitionProxy peer,
                          const Opt_Boolean* isInteractive)
{
    CHECK_NULL_VOID(peer && peer->handler);
    peer->handler->SetInteractive(Converter::OptConvertPtr<bool>(isInteractive).value_or(false));
}
Opt_VoidCallback GetCancelTransitionImpl(Ark_NavigationTransitionProxy peer)
{
    CHECK_NULL_RETURN(peer, {});
    auto callback = CallbackKeeper::ReturnReverseCallback<VoidCallback>([peer]() {
        CHECK_NULL_VOID(peer && peer->handler);
        peer->handler->FireCancelAnimation();
    });
    return Converter::ArkValue<Opt_VoidCallback>(callback);
}
void SetCancelTransitionImpl(Ark_NavigationTransitionProxy peer,
                             const Opt_VoidCallback* cancelTransition)
{
    CHECK_NULL_VOID(peer && peer->handler);
    std::function<void()> cancelAnimation = nullptr;
    std::optional<VoidCallback> callback = Converter::GetOptPtr(cancelTransition);
    if (callback) {
        cancelAnimation = [arkCallback = CallbackHelper(*callback), peer]() {
            CHECK_NULL_VOID(peer && peer->handler);
            peer->handler->CancelInteractiveAnimation();
            arkCallback.InvokeSync();
        };
    }
    peer->handler->SetCancelAnimationCallback(std::move(cancelAnimation));
}
Opt_UpdateTransitionCallback GetUpdateTransitionImpl(Ark_NavigationTransitionProxy peer)
{
    auto callback = CallbackKeeper::ReturnReverseCallback<UpdateTransitionCallback,
        std::function<void(Ark_Float64)>>([peer](Ark_Float64 progress) {
        CHECK_NULL_VOID(peer && peer->handler);
        if (peer->handler->GetInteractive()) {
            peer->FireUpdateProgress(Converter::Convert<float>(progress));
        }
    });
    return Converter::ArkValue<Opt_UpdateTransitionCallback>(callback);
}
void SetUpdateTransitionImpl(Ark_NavigationTransitionProxy peer,
                             const Opt_UpdateTransitionCallback* updateTransition)
{
    CHECK_NULL_VOID(peer && peer->handler);
    std::function<void(const float& progress)> updateTransitionCallback = nullptr;
    std::optional<UpdateTransitionCallback> updateTransitionOpt = Converter::GetOptPtr(updateTransition);
    if (updateTransitionOpt) {
        updateTransitionCallback = [arkCallback = CallbackHelper(*updateTransitionOpt), peer](const float& progress) {
            CHECK_NULL_VOID(peer && peer->handler);
            if (peer->handler->GetInteractive()) {
                peer->handler->UpdateTransition(progress);
                arkCallback.InvokeSync(Converter::ArkValue<Ark_Float64>(progress));
            }
        };
    }
    peer->SetUpdateProgressCallback(std::move(updateTransitionCallback));
}
} // NavigationTransitionProxyAccessor
const GENERATED_ArkUINavigationTransitionProxyAccessor* GetNavigationTransitionProxyAccessor()
{
    static const GENERATED_ArkUINavigationTransitionProxyAccessor NavigationTransitionProxyAccessorImpl {
        NavigationTransitionProxyAccessor::DestroyPeerImpl,
        NavigationTransitionProxyAccessor::ConstructImpl,
        NavigationTransitionProxyAccessor::GetFinalizerImpl,
        NavigationTransitionProxyAccessor::FinishTransitionImpl,
        NavigationTransitionProxyAccessor::GetFromImpl,
        NavigationTransitionProxyAccessor::SetFromImpl,
        NavigationTransitionProxyAccessor::GetToImpl,
        NavigationTransitionProxyAccessor::SetToImpl,
        NavigationTransitionProxyAccessor::GetIsInteractiveImpl,
        NavigationTransitionProxyAccessor::SetIsInteractiveImpl,
        NavigationTransitionProxyAccessor::GetCancelTransitionImpl,
        NavigationTransitionProxyAccessor::SetCancelTransitionImpl,
        NavigationTransitionProxyAccessor::GetUpdateTransitionImpl,
        NavigationTransitionProxyAccessor::SetUpdateTransitionImpl,
    };
    return &NavigationTransitionProxyAccessorImpl;
}

}
